package com.haiheng.voiceandbook.commo

import android.app.Activity
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Build
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import androidx.core.content.FileProvider
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.haiheng.library.log.LogUtils
import com.haiheng.library.log.LogWriter
import com.haiheng.voiceandbook.BuildConfig
import com.haiheng.voiceandbook.Constants
import com.haiheng.voiceandbook.MyApplication
import com.haiheng.voiceandbook.api.ApiService
import com.haiheng.voiceandbook.api.DownLoadService
import com.haiheng.voiceandbook.dialog.UpdateDialog
import com.haiheng.voiceandbook.receiver.DownloadCompleteReceiver
import com.haiheng.voiceandbook.utils.FileUtils
import com.haiheng.voiceandbook.utils.SpUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream


/**
 *
 * @Des:
 * @Author: hwz
 * @Date:2021/12/24 0024
 * @Version: 1.0
 */
class VersionManager  {
    companion object {
        val TAG = "VersionManager"
        private val downloadingUrls = mutableSetOf<String>()

        fun austInstall(context: Context, apkPath: String?){
            val intent = Intent(Intent.ACTION_VIEW)
            intent.setDataAndType(
                Uri.fromFile(File(apkPath)),
                "application/vnd.android.package-archive"
            )
            intent.putExtra("IMPLUS_INSTALL", "SILENT_INSTALL")
            context.startActivity(intent)
        }

        /**
         * 关于在代码中安装 APK 文件，在 Android N 以后，为了安卓系统为了安全考虑，不能直接访问软件
         * 需要使用 fileProvider 机制来访问、打开 APK 文件。
         * 普通安装
         * @param context                   上下文
         * @param apkPath                   path，文件路径
         * @param pathName                  你的包名
         */
        fun installNormal(context: Context, apkPath: String?) {
            if (apkPath == null) {
                return
            }
            val intent = Intent(Intent.ACTION_VIEW)
            val apkFile = File(apkPath)
            // 由于没有在Activity环境下启动Activity,设置下面的标签
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            //版本在7.0以上是不能直接通过uri访问的
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
                val apkUri: Uri =
                    FileProvider.getUriForFile(context, context.getPackageName()+".provider", apkFile)
//                    FileProvider.getUriForFile(context, context.getPackageName()+".fileProvider", apkFile)
                //添加这一句表示对目标应用临时授权该Uri所代表的文件
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
            } else {
                val uri: Uri = Uri.fromFile(apkFile)
                intent.setDataAndType(uri, "application/vnd.android.package-archive")
            }
            intent.putExtra("IMPLUS_INSTALL", "SILENT_INSTALL")
            context.startActivity(intent)
        }

        fun getVersionInfo(acitvity: Context,isShowTip:Boolean) {
            GlobalScope.launch {
                try {
                    var dataDto = ApiService.apiService.getVersionInfo(
                        Constants.VERSION_VER_NO,
                        Constants.VERSION_VER_NAME
                    )
                    if (dataDto != null && dataDto.data != null && !TextUtils.isEmpty(dataDto.data!!.url)) {
                        var s = ApiService.apiService.getVersionConfig(dataDto.data!!.url)
                        s.enqueue(object : Callback<JsonObject> {
                            override fun onFailure(call: Call<JsonObject>, t: Throwable) {
                                LogWriter.e(TAG+  "获取版本信息失败")
                            }

                            override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
                                try {
                                    var versionInfo = response.body().toString();
                                    var versionConfigDto = Gson().fromJson(versionInfo,VersionConfigDto::class.java)
                                    Log.e(TAG, "版本信息:" + versionInfo)
                                    if (versionConfigDto != null){
                                        var isNeedUpdata = false //是否符合更新
                                        if (versionConfigDto.cidlist  != null && versionConfigDto.cidlist.size > 0 ){
                                            var libraryCode = Constants.getLibraryCode()
                                            for (cid in versionConfigDto.cidlist){
                                                //支队配置的馆列表进行更新
                                                if (cid.equals(libraryCode)){
                                                    isNeedUpdata = true
                                                    break
                                                }
                                            }
                                        }else{
                                            isNeedUpdata = true  //没有配置馆列表信息则默认更新全部
                                        }
                                        if (isNeedUpdata){
                                            //小于最低版本就强制更新
                                            if (BuildConfig.VERSION_NAME.compareTo(versionConfigDto.minVersion) < 0){
                                                var apkPath = "${Constants.APK_PATH}/${Constants.APK_NAME}"
                                                if (FileUtils.fileExistByMD5(
                                                        File(apkPath),
                                                        versionConfigDto.md5
                                                    )
                                                ) {
                                                    Log.e(TAG,"文件已经存在，直接安装")
                                                    installNormal(acitvity, apkPath)
                                                } else {
                                                    downloadAPByRange(Constants.APK_NAME,
                                                        versionConfigDto.url,
                                                        versionConfigDto.md5,
                                                        versionConfigDto.fileLength,
                                                        object :DownloadAPKCallBack{
                                                        override fun onResult(data: String) {
                                                            installNormal(acitvity, data)
                                                            removeDownPath(versionConfigDto.url)
                                                        }

                                                        override fun onError(data: String) {
                                                            removeDownPath(versionConfigDto.url)
                                                            Log.e(TAG,"下载apk onError downloadAPK ："+data)
                                                        }

                                                        override fun onLoading(data: String, prosse: Int) {
                                                        }

                                                    })
                                                }

//                                            showHintDialog(acitvity,versionConfigDto.url,true)
                                                return
                                            }
                                            //某个版本以下就提示更新
                                            if (BuildConfig.VERSION_NAME.compareTo(versionConfigDto.notifyVersion) < 0){
                                                //某个版本以下就提示更新
                                                showHintDialog(acitvity,versionConfigDto.url,versionConfigDto.md5,versionConfigDto.fileLength,false)
                                                return
                                            }
                                            //这个是检查更新，点击了才检测，或者wifi下自动检测
                                            if (BuildConfig.VERSION_NAME.compareTo(versionConfigDto.curVersion) < 0){
                                                showHintDialog(acitvity,versionConfigDto.url,versionConfigDto.md5,versionConfigDto.fileLength,false)
                                                return
                                            }
                                            if (isShowTip){
                                                Toast.makeText(acitvity,"当前已是最新版本",Toast.LENGTH_SHORT).show()
                                            }
                                        }else{
                                            Log.d(TAG, "暂无更新信息")
                                        }
                                    }
                                }catch (ex:Exception){
                                    LogWriter.e(TAG+  "信息配置异常:" +ex.message)
                                }
                            }
                        })
                    }
                }catch (ex:Exception){
                    LogWriter.e(TAG+ "信息配置异常:" +ex.message)
                }

            }
        }
        // 保存已下载的大小
        fun saveDownloadedSize(downloadedSize: Long) {
            val file = File(Constants.APK_PATH, "downloaded_size.txt")
            file.writeText(downloadedSize.toString())
        }

        // 读取已下载的大小
        fun readDownloadedSize(): Long {
            val file = File(Constants.APK_PATH, "downloaded_size.txt")
            return if (file.exists()) {
                file.readText().toLong()
            } else {
                0L
            }
        }

        fun removeDownPath(path:String){
            downloadingUrls.remove(path)
        }
        fun downloadAPByRange(fileNam: String, downPath: String, md5: String,fileLength:String, callback: DownloadAPKCallBack?) {
            // 检查是否正在下载相同的链接
            if (downloadingUrls.contains(downPath)) {
                // 如果已经在下载队列中，直接返回，避免重复下载
                LogUtils.d("已经在下载队列中，直接返回，避免重复下载")
                return
            }
            val fileDr = File(Constants.APK_PATH)
            if (!fileDr.exists()) {
                fileDr.mkdir()
            }
            // 获取已下载的文件
            val localFile = File(Constants.APK_PATH, fileNam)
            // 初始化 downloadedSize
            var downloadedSize = 0L

            if (localFile.exists()) {
                // 如果文件存在，获取已下载的文件大小
                downloadedSize = localFile.length()
                if (FileUtils.fileExistByMD5(localFile, md5)) {
                    if (callback != null) {
                        GlobalScope.launch(Dispatchers.Main) {
                            callback.onResult(localFile.absolutePath)
                            return@launch
                        }
                    }
                }else if (downloadedSize >= fileLength.toLong()){
                    //md5校验没通过并且长度一样，说明包有问题 删除后再重新下载
                    localFile.delete()
                }
            }

            GlobalScope.launch(Dispatchers.IO) {
                try {
                    // 发起断点下载请求
                    LogUtils.d("准备下载长度：${downloadedSize}")
                    var result = DownLoadService.downLoadService.downLoadUrl(downPath, "bytes=$downloadedSize-")
                    val len = result.contentLength()

                    LogUtils.d("当前下载长度：${len}")
                    val input = result.byteStream()
                    downloadingUrls.add(downPath)
                    // 将部分内容追加到已下载的文件中
                    writeFileToDisk(input, len, fileNam, callback)

                    // 更新已下载的大小
                    downloadedSize += len
                    val newLocalFil  = File(Constants.APK_PATH, fileNam)
                    LogUtils.d("当前已经下载长度：${localFile.length()} newLocalFil:${newLocalFil.length()} downloadedSize：${downloadedSize} totalSize:$fileLength")
                    // 判断下载是否完成
                    if (localFile.length() >= fileLength.toLong()) {
                        // 下载完成，进行MD5校验
                        LogUtils.d("下载完成，进行MD5校验")
                        if (FileUtils.fileExistByMD5(localFile, md5)) {
                            // MD5校验通过，通知下载完成
                            LogUtils.d("MD5校验通过，通知下载完成：${localFile.absolutePath}")
                            callback?.onResult(localFile.absolutePath)
                        } else {
                            callback?.onError("MD5校验失败,删除本地文件")
//                            if (localFile.exists()) {
//                                localFile.delete()
//                            }
                        }
                    }
                } catch (ex: Exception) {
                    callback?.onError(ex.message.toString())
                }
            }
        }
        fun removeDownloadFromQueue(downloadId: Long) {
            val downloadManager = MyApplication.getInstance().applicationContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
            downloadManager.remove(downloadId)
        }
        fun savedDownloadId(downloadId:Long){
            SpUtil.putLong(MyApplication.getInstance().applicationContext,Constants.SP_DOWNLOAD_INFO,downloadId)
        }

        fun readDownLoadId():Long{
           return SpUtil.getLong(MyApplication.getInstance().applicationContext,Constants.SP_DOWNLOAD_INFO,-1L)
        }


        /**
         * 下载
         * @param fileNam String 文件名称
         * @param downPath String 下载地址
         * @param fileLength Long 文件长度
         * @param callback DownloadAPKCallBack? 下载回调
         */
  /*      fun downloadAPK(fileNam: String, downPath: String,  md5: String,fileLength:String,callback: DownloadAPKCallBack?) {
            var localFile = File(Constants.APK_PATH,fileNam)

            if (FileUtils.fileExistByMD5(localFile, md5)) {
                if (callback != null) {
                    GlobalScope.launch(Dispatchers.Main) {
                        callback.onResult(localFile.absolutePath)
                        return@launch
                    }
                }
            }
            GlobalScope.launch (Dispatchers.IO){
                try {
                    var result =   DownLoadService.downLoadService.downLoadUrl(downPath)
                    val len = result.contentLength()


                    val fileDr = File(Constants.APK_PATH)
                    if (!fileDr.exists()) {
                        fileDr.mkdir()
                    }
                    val file = File(Constants.APK_PATH, fileNam)
                    Log.e(TAG, "当前文件是否存在 ${file.exists()}")
                    if (file.exists()) {
                        file.delete()
                    }
                    val input = result.byteStream()
//            val input: InputStream = BufferedInputStream(url.openStream())
                    //文件存在，则判断存在的文件大小，如果小于，则是不完整的文件
                    if (file.exists()) {
                        //删掉文件
                        file.delete()
                        //写入文件
                        writeFileToDisk(input, len, fileNam,callback)
                    } else {
                        //文件不存在直接写入
                        writeFileToDisk(input, len, fileNam,callback)
                    }
                }catch (ex:Exception){
                    Log.e(TAG,ex.message.toString())
                    withContext(Dispatchers.Main){
                        if (callback != null){
                            callback.onError(ex.message.toString())
                        }
                    }
                }

            }
        }
*/
        /**
         * 将文件 写入磁盘
         */
        suspend fun writeFileToDisk(input: InputStream, size: Long, name: String,callback: DownloadAPKCallBack?) {
            val file = File(Constants.APK_PATH, name)
            if (!file.exists()) {
                file.createNewFile()
            }
            try {
                val fos = FileOutputStream(file,true)
                val bis = BufferedInputStream(input)
                val buffer = ByteArray(1024)
                var len: Int
                var total: Long = 0
                var time = System.currentTimeMillis()
                LogUtils.d("开始写入文件")
                while ((bis.read(buffer).also { len = it } != -1)) {
                    fos.write(buffer, 0, len)
                    total += len
                    if (System.currentTimeMillis() -time > 1000*10){
//                        Log.e(TAG, "写文件----》 ${total}")
                        LogUtils.d("写文件----》：${total}")
                        time = System.currentTimeMillis()
                    }
//                    Log.e(TAG, "写文件----》 ${total}")
                    callback?.let {
                        val totalSile = if (size == 0L) 1L else size
                        it.onLoading("data", ((total * 100) / totalSile).toInt())
                    }
                }
                fos.flush()
                fos.close()
                bis.close()
                input.close()
                Log.e(TAG, "下载完毕${Thread.currentThread().name}")
                withContext(Dispatchers.Main){
                    callback?.onResult(file.absolutePath)
                }

            } catch (e: Exception) {
                LogWriter.e(TAG+ "下载apk异常 writeResponseBodyToDisk: ${e}")
//                file.delete()
                withContext(Dispatchers.Main){
                    callback?.onError(e.message.toString())
                }
            }
        }


        fun showHintDialog(context: Context, url:String,md5: String,fileLength:String,isForceUpdate: Boolean) {
            var updateDialog = UpdateDialog(context,url,md5,fileLength,isForceUpdate)
            updateDialog.show()
        }

    }



}

interface DownloadAPKCallBack {
    fun onResult(data: String)//下载完毕
    fun onError(data: String)//下载错误
    fun onLoading(data: String, prosse: Int)//更新进度条
}